/*
 *  Copyright 2001-2004 The Apache Software Foundation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package ar.uba.fi.posgrado.common.service.impl;

import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import ar.uba.fi.posgrado.common.dao.DAOException;
import ar.uba.fi.posgrado.common.dao.NoSuchEntityException;
import ar.uba.fi.posgrado.common.dao.UserDAO;
import ar.uba.fi.posgrado.common.exception.PosgradoException;
import ar.uba.fi.posgrado.common.exception.PosgradoRuntimeException;
import ar.uba.fi.posgrado.common.exception.RepeatedNameException;
import ar.uba.fi.posgrado.common.model.Role;
import ar.uba.fi.posgrado.common.model.User;
import ar.uba.fi.posgrado.common.service.UserService;
import ar.uba.fi.posgrado.common.util.CommonUtils;

/**
 * Implementacion de {@link UserService user service}.<br>
 * 
 * @author nicolas.gonzalez
 */
public class UserServiceImpl implements UserService {

	/** Logger */
	private static Logger logger = Logger.getLogger(UserServiceImpl.class);

	/** Dao inyectado */
	private UserDAO userDAO;

	/**
	 * Constructor default para spring
	 */
	public UserServiceImpl() {
	}

	/**
	 * Constructor para poder ejecutar los tests
	 * 
	 * @param userDAO
	 */
	public UserServiceImpl(UserDAO userDAO) {
		this.userDAO = userDAO;
	}

	/**
	 * @see ar.uba.fi.posgrado.common.service.UserService#getUserByName(java.lang.String)
	 */
	public User getUserByName(String userName) throws PosgradoRuntimeException,
			PosgradoException {
		try {
			User response = this.userDAO.findByName(userName);
			logger.info("Usuario: " + userName + ", encontrado con exito");
			return response;
		} catch (DAOException e) {
			throw new PosgradoException("Excepcion buscando el usuario: "
					+ userName, e);
		}
	}

	/**
	 * @see ar.uba.fi.posgrado.common.service.UserService#createUser(ar.uba.fi.posgrado.common.model.User)
	 */
	@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
	public void createUser(User user) throws PosgradoRuntimeException,
			RepeatedNameException, PosgradoException {
		try {
			// Puede ser q el nombre de usuario exista
			this.userDAO.findByName(user.getName());

			throw new RepeatedNameException(
					"El nombre de usuario "
							+ user.getName()
							+ " no es un nombre de usuario valido. Ya existe un usuario con"
							+ " ese nombre.");
		} catch (NoSuchEntityException e) {
			// Si entro aca es xq no existia x lo cual esta bien
		}
		try {
			this.userDAO.save(user);
			logger.info("Usuario: " + user + ", creado con exito");
		} catch (DAOException e) {
			throw new PosgradoException(
					"Excepcion creando el usuario: " + user, e);
		}
	}

	/**
	 * @see ar.uba.fi.posgrado.common.service.UserService#createUser(ar.uba.fi.posgrado.common.model.User,
	 *      java.util.List)
	 */
	@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
	@SuppressWarnings("unchecked")
	public void createUser(User user, List<Role> roles)
			throws PosgradoRuntimeException, RepeatedNameException,
			PosgradoException {
		try {
			// Puede ser q el nombre de usuario exista
			this.userDAO.findByName(user.getName());
			throw new RepeatedNameException(
					"El nombre de usuario "
							+ user.getName()
							+ " no es un nombre de usuario valido. Ya existe un usuario con"
							+ " ese nombre.");
		} catch (NoSuchEntityException e) {
			// Si entro aca es xq no existia x lo cual esta bien
		}
		try {
			user.setRoles(CommonUtils.listToSet(roles));

			this.userDAO.save(user);
		} catch (DAOException e) {
			throw new PosgradoException("Excepcion modificando el usuario: "
					+ user, e);
		}
	}

	/**
	 * @see ar.uba.fi.posgrado.common.service.UserService#modifyUser(ar.uba.fi.posgrado.common.model.User)
	 */
	@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
	public void modifyUser(User user) throws PosgradoRuntimeException,
			PosgradoException {
		/*
		 * La transaccionabilidad es REQUIRED porque puede darse el caso que se
		 * este haciendo otras cosas a la hora de modificar un usuario y no es
		 * algo unico quizas.
		 */
		try {
			this.userDAO.update(user);
		} catch (DAOException e) {
			throw new PosgradoException("Excepcion modificando el usuario: "
					+ user, e);
		}
	}

	/**
	 * @see ar.uba.fi.posgrado.common.service.UserService#modifyUser(java.lang.String,
	 *      java.util.List)
	 */
	@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
	@SuppressWarnings("unchecked")
	public void modifyUser(String userName, List<String> roleNames)
			throws PosgradoRuntimeException, PosgradoException {
		try {
			User user = this.userDAO.findByName(userName);
			List<Role> roles = this.userDAO.findRolesByName(roleNames);
			user.setRoles(CommonUtils.listToSet(roles));

			this.userDAO.update(user);
		} catch (DAOException e) {
			throw new PosgradoException("Excepcion modificando el usuario: "
					+ userName, e);
		}
	}

	/**
	 * @see ar.uba.fi.posgrado.common.service.UserService#modifyUser(ar.uba.fi.posgrado.common.model.User,
	 *      java.util.List)
	 */
	@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
	@SuppressWarnings("unchecked")
	public void modifyUser(User user, List<Role> roles)
			throws PosgradoRuntimeException, PosgradoException {
		try {
			user.setRoles(CommonUtils.listToSet(roles));

			this.userDAO.update(user);
		} catch (DAOException e) {
			throw new PosgradoException("Excepcion modificando el usuario: "
					+ user, e);
		}
	}

	/**
	 * 
	 * @see ar.uba.fi.posgrado.common.service.UserService#getRolesByName(java.util.List)
	 */
	public List<Role> getRolesByName(List<String> roleNames)
			throws PosgradoRuntimeException, PosgradoException {
		return this.userDAO.findRolesByName(roleNames);
	}

	/**
	 * @see ar.uba.fi.posgrado.common.service.UserService#deleteUser(ar.uba.fi.posgrado.common.model.User)
	 */
	@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
	public void deleteUser(User user) throws PosgradoRuntimeException,
			PosgradoException {
		try {
			this.userDAO.remove(user);
			logger.info("Usuario: " + user + ", eliminado con exito");
		} catch (DAOException e) {
			throw new PosgradoException("Excepcion eliminando el usuario: "
					+ user, e);
		}
	}

	/**
	 * @see ar.uba.fi.posgrado.common.service.UserService#getAllUsers()
	 */
	public List<User> getAllUsers() throws PosgradoRuntimeException,
			PosgradoException {
		try {
			List<User> response = this.userDAO.findAll();
			logger.info("Usuarios encontrados: " + response.size());
			return response;
		} catch (DAOException e) {
			throw new PosgradoException("Excepcion buscando usuarios", e);
		}
	}

	/**
	 * @see ar.uba.fi.posgrado.common.service.UserService#getAvailableRolesForUser(java.lang.String)
	 */
	public List<Role> getAvailableRolesForUser(String userName)
			throws PosgradoRuntimeException, PosgradoException {
		try {
			User user = this.userDAO.findByName(userName);
			List<Role> response = this.userDAO.getAvailableRoles(user);
			logger.info("Roles disponibles encontrados: " + response.size());
			return response;
		} catch (NoSuchEntityException e) {
			throw new PosgradoException(
					"Excepcion buscando roles disponibles para el usuario: "
							+ userName + ". El usuario no existe.", e);
		} catch (DAOException e) {
			throw new PosgradoException(
					"Excepcion buscando roles disponibles para el usuario: "
							+ userName, e);
		}
	}

	/**
	 * @see ar.uba.fi.posgrado.common.service.UserService#getAllRoles()
	 */
	public List<Role> getAllRoles() throws PosgradoRuntimeException,
			PosgradoException {
		try {
			List<Role> response = this.userDAO.findAllRoles();
			logger.info("Roles encontrados: " + response.size());
			return response;
		} catch (DAOException e) {
			throw new PosgradoException("Excepcion buscando roles", e);
		}
	}

	/**
	 * @param userDAO
	 *            the userDAO to set
	 */
	public void setUserDAO(UserDAO userDAO) {
		this.userDAO = userDAO;
	}

	public boolean hasBodyRole(List<Role> roles) {
		for (Role role : roles) {
			if (role.equals(Role.getBodyRole())) {
				return true;
			}
		}
		return false;
	}
}
